package sim;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.DataInputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.InputStreamReader;
import java.util.LinkedList;
import java.util.HashSet;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.PriorityQueue;

public class Experiment {
	HashMap<String, Integer> routerIndex;
	HashMap<Integer, String> routerName;
	public static PriorityQueue<Event> eventHeap;
	Hashtable<Integer, LinkedList<Adjacency>> adjMatrix;
	static int numOfRouters;
	Hashtable<String, HashSet<Integer>> statsTable;
//	Hashtable<String, Integer> diameterTable;
	
	public Experiment(String filename) throws FileNotFoundException{
		routerIndex = new HashMap<String, Integer>();
		routerName = new HashMap<Integer, String>();
		eventHeap = new PriorityQueue<Event>();
		adjMatrix = new Hashtable<Integer, LinkedList<Adjacency>>();
		numOfRouters = 0;
		statsTable = new Hashtable<String, HashSet<Integer>>();
//		diameterTable = new Hashtable<String, Integer>();
		
		//Scan file and build router map
		try {
			FileInputStream fstream = new FileInputStream(filename);
			//Get the object of DataInputStream
			DataInputStream in = new DataInputStream(fstream);
			BufferedReader br = new BufferedReader(new InputStreamReader(in));
			String strLine;
			//Read File Line By Line
			while ((strLine = br.readLine()) != null){
				//Split the line
				String values[] = strLine.split(" ");
				String rtr1 = values[0];
				
				//Check existence of routers and update maps accordingly
				int index1;
				//If new router, update maps
				if (!routerIndex.containsKey(rtr1)){
					index1 = numOfRouters++;
					//System.out.println(numOfRouters);
					routerIndex.put(rtr1, index1);
					routerName.put(index1, rtr1);
				}
			}
			//Close the input stream
			in.close();
		} catch (FileNotFoundException e){
//			System.err.println("File not found.");
			throw e;
		} 
		catch (Exception e){
			//Catch exception if any
			System.err.println("Experinter Initialization - First Parsing Pass Error: " + e.getMessage());
//			e.printStackTrace();
			System.exit(1);
		}
		
		//Scan file and build adjacency matrix
		try {
			FileInputStream fstream = new FileInputStream(filename);
			//Get the object of DataInputStream
			DataInputStream in = new DataInputStream(fstream);
			BufferedReader br = new BufferedReader(new InputStreamReader(in));
			String strLine;
			//Read File Line By Line
			while ((strLine = br.readLine()) != null){
				//Split the line
				String values[] = strLine.split(" ");
				String rtr1 = values[0];
				String rtr2 = values[1];
				float cost = Float.parseFloat(values[2]);
				
				//Check existence of routers and update maps accordingly
				int index1, index2;
				index1 = routerIndex.get(rtr1);
				index2 = routerIndex.get(rtr2);
				
				//Update adjMatrix with cost (directed links)
				if (!adjMatrix.containsKey(index1)){
					LinkedList<Adjacency> newAdj = new LinkedList<Adjacency>();
					newAdj.add(new Adjacency(index2, cost));
					adjMatrix.put(index1, newAdj);
				} else {
					LinkedList<Adjacency> adj = adjMatrix.get(index1);
					adj.add(new Adjacency(index2, cost));
					adjMatrix.put(index1, adj);
				}
			}
			//Close the input stream
			in.close();
		} catch (Exception e){
			//Catch exception if any
			System.err.println("Experiment Initialization - Second Parsing Pass Error: " + e.getMessage());
			e.printStackTrace();
			System.exit(1);
		}
		
	}

	public void run(int attacker, int victim) {
		//Check validity of parameters (Is there an attacker, DR etc.?)
		if (attacker >= numOfRouters || victim >= numOfRouters || attacker < 0 || victim < 0 || attacker == victim){
			System.out.println("Error: invalid parameter values!");
			return;
		}
//		int unaffectedDiameter = 0;		
		//Initialize router DS and create initial event heap
		Router.init(attacker, victim, numOfRouters, adjMatrix);
		eventHeap.add(new SendEvent(Common.time, attacker, new LSA(0, LSAType.Trigger)));
		eventHeap.add(new SendEvent(Common.time + Common.SEND_DELTA, attacker, new LSA(1, LSAType.FalseLSA)));
		
		while (!eventHeap.isEmpty()){
			Event current = eventHeap.remove();
			Common.time = current.time;
			current.execute();
		}
		
		//Update experiment statistical data for report generation
		for (int i = 0 ; i < numOfRouters ; i++){
			if (i == attacker) continue;
			if (Router.memory[i].type != LSAType.FalseLSA){
				//Update statsTable with new impossible spoof
				String key = attacker+"_"+i;
				if (!statsTable.containsKey(key)){
					HashSet<Integer> victims = new HashSet<Integer>();
					victims.add(victim);
					statsTable.put(key, victims);
				} else {
					HashSet<Integer> victims = statsTable.get(key);
					victims.add(victim);
					statsTable.put(key, victims);
				}
/*/!@#$				
				//Diameter of unaffected area
				unaffectedDiameter = 0;
				for (int r1 = 0; r1 < numOfRouters ; r1++){
					if (Router.memory[r1].type != LSAType.Fightback) continue;
					for (int r2 = 0 ; r2 < numOfRouters ; r2++){
						if (Router.memory[r2].type != LSAType.Fightback || r2 == r1) continue;
						int currDist = Router.distance(r1, r2);
						if (currDist > unaffectedDiameter){
							unaffectedDiameter = currDist;
						}
					}
				}				
				diameterTable.put(key, unaffectedDiameter);
//~!@#$	*/			
			}
		}
	}
	
	public void generateReportScope(String logScope) {
		String attacker = routerName.get(Router.attacker);
		String victim = routerName.get(Router.victim);
		
		//Statistics generation for first file
		int unanfectedRouters = 0;
		int unaffectedDiameter = 0;
		
		//No. of infected routers
		for (int i=0 ; i<numOfRouters ; i++){
			if (Router.memory[i].type != LSAType.FalseLSA) {unanfectedRouters++;}
		}
		//Diameter of unaffected area
		int notReachable = 0;
		for (int r1 = 0; r1 < numOfRouters ; r1++){
			if (Router.memory[r1].type != LSAType.Fightback) continue;
			if (-1 == Router.distance(Router.victim, r1)) notReachable++;
			for (int r2 = 0 ; r2 < numOfRouters ; r2++){
				if (Router.memory[r2].type != LSAType.Fightback || r2 == r1) continue;
				int currDist = Router.distance(r1, r2);
				if (currDist > unaffectedDiameter){
					unaffectedDiameter = currDist;
				}
			}
		}

		try{
			//Write to first file
			FileWriter fstream = new FileWriter(logScope, true);
			BufferedWriter out = new BufferedWriter(fstream);
			out.write(attacker+"\t"+victim+"\t"+(unanfectedRouters-notReachable)+"\t"+unaffectedDiameter+"\n");
			out.close();
			
		} catch (Exception e){//Catch exception if any
			System.err.println("Report Generation Error - Scope File Error: " + e.getMessage());
		}
	}

	public void generateReportGroup(String logSpoofGroup) {
		
		//Statistics generation for second file
		for (int attackerIndex = 0 ; attackerIndex < numOfRouters ; attackerIndex++){
			if (Common.debug) System.out.println("attackerIndex:"+attackerIndex);
			for (int victimIndex = 0 ; victimIndex < numOfRouters ; victimIndex++){
				if (attackerIndex == victimIndex) continue;
				String key = attackerIndex+"_"+victimIndex;
				if (statsTable.containsKey(key)){
					HashSet<Integer> spoofSet = statsTable.get(key);
					
					//Calculate spoofSet diameter
					int spoofSetDiameter = 0;
					for (Integer r1:spoofSet){
						for (Integer r2:spoofSet){
							if (r1 == r2) continue;
							int currDist = Router.distance(r1, r2);
							if (currDist > spoofSetDiameter){
								spoofSetDiameter = currDist;
							}
						}
					}
//					int spoofSetDiameter = 0;
					int notReachable =0;
					for (Integer r1:spoofSet){
						int currDist = Router.distance(victimIndex, r1);
						if (-1 == currDist) notReachable++;
//						if (currDist > spoofSetDiameter){
//							spoofSetDiameter = currDist;
//						}
					}
					
					try{
						//Write to second file
						FileWriter fstream = new FileWriter(logSpoofGroup, true);
						BufferedWriter out = new BufferedWriter(fstream);
						out.write(routerName.get(attackerIndex)+"\t"+routerName.get(victimIndex)+"\t"+(spoofSet.size()-notReachable)+"\t"+spoofSetDiameter+"\n");
						out.close();
						
					} catch (Exception e){//Catch exception if any
						System.err.println("Report Generation Error - File CC File Error: " + e.getMessage());
					}
				}
			}
		}
	}	
	
}
